package com.edu.juc.threadLocal.reference;

import com.edu.juc.threadLocal.bean.MyObject;

import java.lang.ref.PhantomReference;
import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;

/*
 1. 虚引用必须和引用队列(ReferenceQueue)一起使用。
    虚引用需要java.lang.ref.ReferenceQueue类来实现，顾名思义就是形同虚设，
    与其他几种引用不同，虚引用并不会决定对象的生命周期。
    如果一个对象仅持有虚引用，那么它就和没有任何引用一样，在任何时候都可能被垃圾回收器回收，
    它不能单独使用也不能通过它访问对象。
  2. PhantomReference的get方法总是返回null。
    虚引用的主要作用是跟踪对象被垃圾回收的状态。仅仅是提供了一种确保对象被finalize以后，做某些事情的通知机制。
    PhantomReference的get方法总是返回null，也因此无法访问对应的引用对象。
  3. 处理监控通知使用
    换句话说，设置虚引用关联对象的唯一目的，就是在这个对象被收集器回收的时候收到一个系统通知或者后续添加进一步的处理，
    用来实现比finalize更灵活的回收行为。
 */
public class PhantomReferenceTest {
    public static void main(String[] args) {
        MyObject myObject = new MyObject();
        ReferenceQueue<MyObject> referenceQueue = new ReferenceQueue<>();

        // 创建一个新的幻象引用，该引用引用给定对象并在给定队列中注册。
        PhantomReference<MyObject> phantomReference = new PhantomReference<>(myObject, referenceQueue);

        System.out.println(phantomReference.get()); // null

        List<byte[]> list = new ArrayList<>();
        new Thread(() -> {
            while (true) {
                list.add(new byte[1024 * 1024]);
                try {
                    TimeUnit.MILLISECONDS.sleep(500);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }

                System.out.println(phantomReference.get() + "--list add ok");
            }
        }, "t1").start();

        new Thread(() -> {
            while (true) {
                Reference<? extends MyObject> reference = referenceQueue.poll();
                if (reference != null) {
                    System.out.println("phantomReference.get() = " + phantomReference.get());
                    System.out.println("t2 线程收到通知，引用对象被回收了");
                    break;
                }
            }
        }, "t2").start();
    }
}
